#include "Thread.hpp"
#include "RingQueue.hpp"
#include <iostream>
#include <vector>
#include <functional>
#include <unistd.h>
#include <ctime>

using namespace ThreadModule;

using ringqueue = RingQueue<int>; // int用于测试



void Consum(ringqueue &rq, const std::string &name)
{
    while (true)
    {
        sleep(2);
        int data = 0;
        rq.Pop(&data);
        std::cout << "Consumer : " << data << " [" << name << "]" << std::endl;
    }
}

void Product(ringqueue &rq, const std::string &name)
{
    // 种一颗随机数种子
    srand(time(nullptr) ^ pthread_self()); // 按位或上pthread_self会让随机数更随机
    int cnt = 10;
    while (true)
    {
        rq.Enqueue(cnt);
        std::cout << "Productor : " << cnt << " [" << name << "]" << std::endl;
        cnt--;        
    }
}

void InitComm(std::vector<Thread<ringqueue>> *threads, int num, ringqueue &rq, func_t<ringqueue> func, const std::string nm)
{
    // 创建线程
    for (int i = 0; i < num; i++)
    {
        std::string name = "thread-" + std::to_string(i + 1) + nm;
        threads->emplace_back(func, rq, name);
        // threads->back().Start();  // 启动线程
        // 不能这么start线程，因为线程在start的时候才真正被创建了，
        // 而主线程在start这个线程后，会回过头继续用这句代码threads->emplace_back(func, rq, name);创建新线程，
        // 所以这个threads尾部是不固定的，有可能这个back还没有构建好，所以我们去访问它里面的数据就可能是空
        // 即传数据的时候可能出现了多线程并发访问一个类内的属性问题
    }
}
void InitProduct(std::vector<Thread<ringqueue>> *threads, int num, ringqueue &rq)
{
    InitComm(threads, num, rq, Product, "-Productor");
}
void InitConsume(std::vector<Thread<ringqueue>> *threads, int num, ringqueue &rq)
{
    InitComm(threads, num, rq, Consum, "-consumer");
}
void WaitAllThread(std::vector<Thread<ringqueue>> &threads)
{
    for (auto &thread : threads)
    {
        thread.Join();
    }
}
void StartAll(std::vector<Thread<ringqueue>> &threads)
{
    for(auto &thread:threads)
    {
        std::cout << "start: " << thread.name() << std::endl;
        thread.Start();
    }
}

int main()
{
    ringqueue *bq = new ringqueue(10);
    std::vector<Thread<ringqueue>> threads;

    InitProduct(&threads, 3, *bq);
    InitConsume(&threads, 5, *bq);

    StartAll(threads);
    WaitAllThread(threads);
    return 0;
}
